package com.blue.core.util;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;

/**
 * JavaBean工具类
 * 
 * @author zhengj
 * @since 1.0 2014-1-19
 */
public class BeanUtil
{
	private static Logger logger = LoggerFactory.getLogger(BeanUtil.class);
	
	private BeanUtil()
	{
	}
	
	
	public static <T> T createBean(Class<T> clazz, Map<String, ?> map)
	{
		T obj = null;
		try
		{
			obj = clazz.newInstance();
			for (Method setter : ReflectionUtil.setterList(clazz))
			{
				String field = ReflectionUtil.field(setter);
				Object value = map.get(field);
				if (value == null || value.equals(""))
					continue;
				
				Class<?>[] params = setter.getParameterTypes();
				if (params.length != 1)
					continue;
				
				Class<?> param = params[0];
				if (param == value.getClass())
				{
					setter.invoke(obj, value);
				}
			}
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
		return obj;
	}
	
	
	public static void copyProperties(Object src, Object dest, boolean nullable)
	{
		Class<?> srcCls = src.getClass();
		Class<?> destCls = dest.getClass();
		
		List<Method> getterList = new ArrayList< Method>();
		for (Method m : srcCls.getMethods())
		{
			if (m.getParameterTypes().length == 0 && (m.getName().startsWith("is") || m.getName().startsWith("get")))
			{
				getterList.add(m);
			}
		}
		Map<String, Method> setterMap = new HashMap<String, Method>();
		for (Method m : destCls.getMethods())
		{
			if (m.getParameterTypes().length == 1 && m.getName().startsWith("set"))
			{
				setterMap.put(ReflectionUtil.field(m), m);
			}
		}
		
		for (Method m : getterList)
		{
			try
			{
				Object value = m.invoke(src);
				
				if (value == null && !nullable)
					continue;
				
				String field = ReflectionUtil.field(m);
				Method setter = setterMap.get(field);
				if (setter != null)
				{
					setter.invoke(dest, value);
				}
			}
			catch (Exception e)
			{
				e.printStackTrace();
			}
			
			
		}
		
	}
	
	/**
	 * 使用Spring的IoC容器实例化并初始化指定类
	 * 
	 * @param clazz 类
	 * @param target 目标类型
	 * @param ctx Spring上下文对象
	 * @return 实例化后的类
	 */
	@SuppressWarnings("unchecked")
	public static <T> T initBean(Class<?> clazz, Class<T> target, ApplicationContext ctx)
	{
		String className = clazz.getName();
		T obj = null;
		try
		{
			obj = (T)clazz.newInstance();
			inject(obj, clazz, ctx);
			init(obj);
		}
		catch (SecurityException e)
		{
			logger.error("无法访问类：" + className, e);
		}
		catch (InstantiationException e)
		{
			logger.error("无法实例化类，可能缺少无参构造方法：" + className, e);
		}
		catch (IllegalAccessException e)
		{
			logger.error("无法访问：" + className, e);
		}
		catch (Exception e)
		{
			logger.error("InitializingBean.afterPropertiesSet()抛出", e);
		}
		
		return obj;
	}
	
	/**
	 * 使用Spring的IoC容器实例化并初始化指定类
	 * 
	 * @param clazz 类
	 * @param ctx Spring上下文对象
	 * @return 实例化后的类
	 */
	public static <T> T initBean(Class<T> clazz, ApplicationContext ctx)
	{
		return initBean(clazz, clazz, ctx);
	}
	
	/**
	 * 使用Spring的IoC容器实例化并初始化指定类
	 * 
	 * @param className 类名
	 * @param target 目标类型
	 * @param ctx Spring上下文对象
	 * @return 实例化后的类
	 */
	public static <T> T initBean(String className, Class<T> target, ApplicationContext ctx)
	{
		T obj = null;
		try
		{
			Class<?> clazz = Class.forName(className, true, Thread.currentThread().getContextClassLoader());
			obj = initBean(clazz, target, ctx);
		}
		catch (ClassNotFoundException e)
		{
			logger.error("找不到类：" + className, e);
		}
		
		return obj;
	}
	
	
	
	/**
	 * 实例化指定的类
	 * 
	 * @param <T> 类型参数
	 * @param className 类
	 * @param target 目标类型
	 * @return 实例化后的类
	 * @throws ClassNotFoundException 找不到类时抛出
	 * @throws InstantiationException 无法实例化时抛出
	 * @throws IllegalAccessException 无法访问时抛出
	 */
	@SuppressWarnings("unchecked")
	public static <T> T getBean(String className, Class<T> target)
			throws ClassNotFoundException, InstantiationException, IllegalAccessException
	{
		Class<?> clazz = Class.forName(className, true, Thread.currentThread().getContextClassLoader());
		Object obj = clazz.newInstance();
		return (T)obj;
	}
	
	/**
	 * 调用 Bean初始化 方法
	 * 
	 * @param target 目标对象
	 * @throws Exception InitializingBean.afterPropertiesSet()抛出
	 */
	public static void init(Object target) throws Exception
	{
		if (target instanceof InitializingBean)
		{
			InitializingBean initBean = (InitializingBean)target;
			initBean.afterPropertiesSet();
		}
	}

	/**
	 * 注入属性
	 * 
	 * @param target 目标对象
	 * @param ctx Spring上下文对象
	 * @throws SecurityException 类安全问题时抛出
	 * @throws IllegalAccessException 无法访问类属性时抛出
	 */
	public static void inject(Object target, ApplicationContext ctx)
			throws SecurityException, IllegalAccessException
	{
		Class<?> clazz = target.getClass();
		inject(target, clazz, ctx);
	}
	
	private static void inject(Object target, Class<?> clazz, ApplicationContext ctx)
			throws SecurityException, IllegalAccessException
	{
		if (clazz == Object.class)
			return;
		
		Field[] fields = clazz.getDeclaredFields();
		Field.setAccessible(fields, true);
		for (Field field : fields)
		{
			Resource resourceAnno = field.getAnnotation(Resource.class);
			if (resourceAnno != null)
			{
				logger.debug(field.getName() + " 有 Reource 注解");
				setFieldValue(target, ctx, field, resourceAnno);
				continue;
			}

			Autowired autowiredAnno = field.getAnnotation(Autowired.class);
			if (autowiredAnno != null)
			{
				logger.debug(field.getName() + " 有 Autowired 注解");
				setFieldValue(target, ctx, field, autowiredAnno);
			}
		}
		inject(target, clazz.getSuperclass(), ctx);
	}

	private static void setFieldValue(Object target, ApplicationContext ctx, Field field,
			Resource resourceAnno) throws IllegalAccessException
	{
		String name = resourceAnno.name();
		Class<?> fieldClass = field.getType();
		Object fieldValue = null;
		if (name == null || name.equals(""))
		{
			name = field.getName();
			fieldValue = ctx.getBean(name, fieldClass);
			if (fieldValue == null)
				fieldValue = ctx.getBean(fieldClass);

		}
		else
		{
			fieldValue = ctx.getBean(name, fieldClass);
		}
		if (fieldValue == null)
			throw new IllegalAccessException(target.getClass().getName() + " 中的 "
					+ field.getName() + " 属性无注入");

		field.set(target, fieldValue);
	}

	private static void setFieldValue(Object target, ApplicationContext ctx, Field field,
			Autowired autowiredAnno) throws IllegalAccessException
	{
		Qualifier qualifierAnno = field.getAnnotation(Qualifier.class);
		Class<?> fieldClass = field.getType();
		Object fieldValue = null;
		if (qualifierAnno == null)
		{
			fieldValue = ctx.getBean(fieldClass);
		}
		else
		{
			logger.debug(field.getName() + " 有 Qualifier 注解");
			String name = qualifierAnno.value();
			if (name == null || name.equals(""))
			{
				name = field.getName();
				fieldValue = ctx.getBean(name, fieldClass);
				if (fieldValue == null)
					fieldValue = ctx.getBean(fieldClass);
			}
			else
			{
				fieldValue = ctx.getBean(name, fieldClass);
			}
		}
		if (autowiredAnno.required() && fieldValue == null)
			throw new IllegalAccessException(target.getClass().getName() + " 中的 "
					+ field.getName() + " 属性无注入");

		if (fieldValue != null)
			field.set(target, fieldValue);
	}
}
